﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace GeoAPI
{
	public static class GeometryServiceProvider
	{
		public static IGeometryServices Instance
		{
			get
			{
				IGeometryServices result;
				lock (GeometryServiceProvider.LockObject)
				{
					IGeometryServices geometryServices;
					if ((geometryServices = GeometryServiceProvider._instance) == null)
					{
						geometryServices = (GeometryServiceProvider._instance = GeometryServiceProvider.ReflectInstance());
					}
					result = geometryServices;
				}
				return result;
			}
			set
			{
				if (value == null)
				{
					throw new System.ArgumentNullException("value", "You must not assign null to Instance!");
				}
				lock (GeometryServiceProvider.LockObject)
				{
					GeometryServiceProvider._instance = value;
				}
			}
		}

		private static IEnumerable<Type> GetLoadableTypes(Assembly assembly)
		{
			if (assembly == null)
			{
				return new Type[0];
			}
			IEnumerable<Type> result;
			try
			{
				result = assembly.GetExportedTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				Type[] types = ex.Types;
				IList<Type> list = new List<Type>(types.Length);
				foreach (Type type in types)
				{
					if (type != null && type.IsPublic)
					{
						list.Add(type);
					}
				}
				result = list;
			}
			catch
			{
				result = new Type[0];
			}
			return result;
		}

		private static IGeometryServices ReflectInstance()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (!(assembly is AssemblyBuilder) && !(assembly.GetType().FullName == "System.Reflection.Emit.InternalAssemblyBuilder") && (!assembly.GlobalAssemblyCache || !(assembly.CodeBase == Assembly.GetExecutingAssembly().CodeBase)))
				{
					foreach (Type type in GeometryServiceProvider.GetLoadableTypes(assembly))
					{
						if (!type.IsInterface && !type.IsAbstract && !type.IsNotPublic && typeof(IGeometryServices).IsAssignableFrom(type))
						{
							ConstructorInfo[] constructors = type.GetConstructors();
							foreach (ConstructorInfo constructorInfo in constructors)
							{
								if (constructorInfo.IsPublic && constructorInfo.GetParameters().Length == 0)
								{
									return (IGeometryServices)Activator.CreateInstance(type);
								}
							}
						}
					}
				}
			}
            throw new System.InvalidOperationException("Cannot use GeometryServiceProvider without an assigned IGeometryServices class");
		}

		private static IGeometryServices _instance;

		private static readonly object LockObject = new object();
	}
}
